<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
</head>

<body>
  <style lang="scss">
    /* scss练习*/

    /* @mixin important-text {
  color: red;
  font-size: 25px;
  font-weight: bold;
  border: 1px solid blue;
}
.content{
  @include important-text;
} */

    /* css3动画 */

    @keyframes myfirst {
      0% {
        background: red;
      }

      25% {
        background: yellow;
      }

      50% {
        background: blue;
      }

      100% {
        background: green;
      }
    }

    .container {
      /* animation: myfirst 5s linear 0s 1 alternate; */
      /* filter: blur(5px);模糊
       */
      /* filter: grayscale();灰度 */
      margin-left: 50%;
      margin-top: 100px;
      transform: translateX(-50%);
      display: flex;
      /* flex-direction: row;
      flex-wrap: wrap;
      justify-content: space-between;
      align-content: space-between;
      align-self: center; */
      overflow: hidden;
      /* 超出文字显示...*/
      text-overflow: ellipsis;
      white-space: nowrap;

      width: 400px;
      height: 400px;
      /* background-color: #eee; */
      background: linear-gradient(#e66465, #9198e5);
      transition: all 1s;
      box-sizing: border-box;
    }

    .container:hover {
      transform: scale(1.2, 1.2);
    }

    .item {
      flex: 1;
      height: 100px;
      border-radius: 50%;
      border: 1px solid white;
    }

    .test {
      width: 0;
      height: 0;
      border-left: 20px solid transparent;
      border-right: 20px solid transparent;
      border-top: 50px solid red;
    }
  </style>

  <div class="container">
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
  </div>

  <div class="test"></div>
  <script>
    class HD {
      constructor(executor) {
        this.state = 'pending';
        this.value = null;
        this.callbacks = [];
        function resolve(value) {
          if (this.state === 'pending') {
            this.state = 'resolve';
            this.value = value;
            setTimeout(() => {
              this.callbacks.map((item) => {
                item.onResove && item.onResove();
              });
            });
          }
        }
        function reject(value) {
          if (this.state === 'pending') {
            this.state = 'reject';
            this.value = value;
            setTimeout(() => {
              this.callbacks.map((item) => {
                item.onReject && item.onReject();
              });
            });
          }
        }
        try {
          executor(resolve.bind(this), reject.bind(this));
        } catch (error) {
          reject(error);
        }
      }

      then(onResove, onReject) {
        if (onResove != 'function') {
          onResove = (value) => value;
        }
        if (onReject != 'function') {
          onReject = (value) => value;
        }
        function callFn(typeFn) {
          let result = typeFn(this.value);
          if (thenResult === result) throw new TypeError('Chaining cycle detected for promise');
          try {
            if (result instanceof HD) {
              result.then(
                (r) => {
                  resolve(r);
                },
                (v) => {
                  reject(v);
                }
              );
            } else {
              resolve(result);
            }
          } catch (error) {
            reject(error);
          }
        }
        let thenResult = new Hd((resolve, reject) => {
          if (this.state === 'resolve') {
            setTimeout(() => {
              callFn(onResove);
            });
          }
          if (this.state === 'reject') {
            setTimeout(() => {
              callFn(onReject);
            });
          }
          if (this.state === 'pending') {
            this.callbacks.push({
              onResove: () => {
                callFn(onResove);
              },
              onReject: () => {
                callFn(onReject);
              },
            });
          }
        });

        return thenResult;
      }

      catch(onReject) {
        return this.then(undefined, onReject);
      }

      static resolve(value) {
        return new Hd((resolve, reject) => {
          try {
            if (value instanceof Hd) {
              value.then(resolve, reject);
            } else {
              resolve(value);
            }
          } catch (error) {
            reject(error);
          }
        });
      }
      static reject(value) {
        return new Hd((resolve, reject) => {
          reject(value);
        });
      }

      static race(proArrs) {
        return new Hd((resolve, reject) => {
          proArrs.forEach((proArr) => {
            proArr.then(resolve, reject);
          });
        });
      }
      static all(proArrs) {
        let arrayResult = [];
        return new Hd((resolve, reject) => {
          proArrs.forEach((item, index) => {
            item.then(
              (r) => {
                arrayResult[index] = r;
                if (arrayResult.length === proArrs.length) {
                  resolve(arrayResult);
                }
              },
              (v) => {
                reject(v);
              }
            );
          });
        });
      }
    }

    // 做用slice函数做分页分割时，或者数组分割时 前一个永远比后一个参数page少1  slice(index*size,(index+1)*size)
    // 前端分页 slice((page-1)*size,page*size)
    // 条件1 ? 表达式1 : 条件2 ? 表达式2 : 条件3 ? 表达式3 : 表达式4
    //随机生成字符串＋数字Math.random().toString(36).slice(2)

    /** === 进行比较时，如果两边数据类型不一样，先转化成相同护具类型。规则如下：**/
    // 1.{} =={}  两个对象（obj，fun，arr，date，reg）比较时，比较的是堆内存的地址
    // 2.null == undefined成立/ null===undefined不成立
    // 3.NaN==NaN不成立 NaN和谁都不相等
    // 4.对象与字符串比较时，调用对象原型的toString()方法转成字符串在进行比较
    // 5.剩余情况比较时，都是转成数字进行比较（前提是数据类型不一样）

    // 页面（window）滚动条到底部的条件即为：scrollTop + clientHeight == scrollHeight

    // 观察某个元素el 是否在屏幕可视区范围
    // const ob = new IntersectionObserver((e)=>{
    //   e.isIntersecting  是否在可视区内
    // })
    //建立观察ob.observe(el)
    //取消观察ob ob.unobserve(el)
    // new MutationObserver() //观察整个文档，组件 元素的属性 节点 文本的变化 异步方法(微队列)
    // new ResizeObserver() //观察具体元素 组件的大小的变化
    // new IntersectionObserver

    // 动画定时器 间隔时间为屏幕刷新帧数 用法与setTimeout一样
    // requestAnimationFrame(() => {});渲染函数 0.06
    // cancelAnimationFrame(); 0.4

    // el.getBoundingClientRect() 返回元素距离视口的空间位置对象

    // 通过FileReader.readAsDataURL(file)可以获取一段data:base64的字符串
    // 通过URL.createObjectURL(blob)可以获取当前文件的一个内存URL

    // ['1','2','3'].map(parseInt) =>[1,NaN,NaN]
    // parseInt() 接受两个参数，第二个是标识是什么进制的数，必须在（2-36）之间， 0表示10进制 2表示2进制 1会返回NaN

    // 0.1+0.2=0.3000000000004 他们转化成二进制是无限循环！ IEEE754标准 64位双精度表示（符号位 0正数 1负数，指数位，有效位，）    
    //第0位为符号位，0表示正数1表示负数；第1到11位存储指数部分；第12到63位存小数部分（即有效数字 53个二进制位）。0-63 共64位



    /**事件循环机制**/

    // ======浏览器
    //  1先主代码
    //  2微任务
    //  3宏任务
    // 注意：每次执行完一个宏任务 就会立刻去查找执行微任务
    // ======Node事件
    // process.nextTick方法可以在当前"执行栈"的尾部----下一次Event Loop（主线程读取"任务队列"）之前----触发回调函数。也就是说，它指定的任务总是发生在所有异步任务之前。
    // setImmediate方法则是在当前"任务队列"的尾部添加事件，也就是说，它指定的任务总是在下一次Event Loop时执行

    //  1先主代码
    // 2.  process.nextTick()
    // 3.  Promise回调     2 3 都是微任务
    // timer模块 setTimeout(()=>{})0ms
    // i/o I/O 回调。
    // check模块 setimmediate()
    // close callbacks

    // 执行上下文创建阶段：作用域链、变量对象、this
    // 执行上下文执行阶段：变量赋值、函数引用

    //console.log(self); // self不管在哪儿始终指向window
    //globalThis node 和浏览器中只想全局对象
    /**数组改变**/
    //reverse  sort push pop  shift unshift splice  这7个数组方法都是会改变原始数组
    // concat slice map filter 都是得到新的数组 不会改变原数组

    /**分片 断点 上传大文件思路**/
    // 前端用file.slice()分片
    // 前端用FileReader获取每一分块的md5值 （spark-MD5插件）
    // 前端上传每一分块前，都会去服务器校验分片是否已上传，以此来支持断点续传
    // 后端用MultipartFile接受分块文件
    // 后端用FileOutputStream拼装分块文件

    // 将文件切成多个小的文件；利用MD5 （spark-MD5插件）, MD5 是文件的唯一标识，可以利用文件的 MD5 查询文件的上传状态；
    // 将切片并行上传 ,前端上传每一分块前，都会通过MD5去服务器校验分片是否已上传，以此来支持断点续传 前端上传每一分块前，都会去服务器校验分片是否已上传，以此来支持断点续传；
    // 所有切片上传完成后，服务器端进行切片合成；
    // 当分片上传失败，可以在重新上传时进行判断，只上传上次失败的部分实现断点续传；
    // 当切片合成为完整的文件，通知客户端上传成功；
    // 已经传到服务器的完整文件，则不需要重新上传到服务器，实现秒传功能；

    /**父子组件的加载顺序**/
    //home beforeCreate -> home created -> home beforeMount ->list beforeCreate->list created -> list beforeMount -> list mounted->home mounted
    //普通for定length 》 普通for不定length  》forEach 》for of 》map，filter》for in

    /**  require和import的区别：**/
    // 导入require 导出 exports/module.exports 是 CommonJS 的标准，通常适用范围如 Node.js
    // import/export 是 ES6 的标准，通常适用范围如 React
    // require 是赋值过程并且是运行时才执行，值的浅拷贝 同步加载
    // require 可以理解为一个全局方法，因为它是一个方法所以意味着可以在任何地方执行。
    // import 是解构过程并且是编译时执行，值的引用(符号绑定) 同步加载异步加载.
    // import 会提升到整个模块的头部，具有置顶性，但是建议写在文件的顶部。
    // require 是浅拷贝，也就是说你可以修改对象第二层的属性并影响原数据，import 是引用(名字引用) 修改基本数据 引用数据都会变
    // require 的性能相对于 import 稍低。因为 require 是在运行时才引入模块并且还赋值给某个变量，而 import 只需要依据 import 中的接口在编译时引入指定模块所以性能稍高

    // 1、停止断点调试
    // 2、不跳入函数中去,继续执行下一行代码(F10)
    // 3、跳入函数中去(F11)
    // 4、从执行的函数中跳出
    // 5、禁用所有的断点,不做任何调试
    // 6、程序运行时遇到异常时是否中断的开关

    // 不要再forEach循环中执行异步任务(Promise)，经过查阅资料后发现,如果有异步会并行

    //  webpack4安装scss时 node-sass只能在4的版本并且sass-loader不能超过8的版本，比如：sass-loader@7.3.1 node-sass@4.14.1
    //  webpack5只支持sass-loader@8.0.2以上
    //一般现在会用dart-sass代替node-sass

    // $attrs对象，父组件向子组件传递的所有attr属性（没有在子组件props声明的属性），
    // 默认会绑定在子组件的根元素上，可以设置inHeritAttrs为false不绑定在根组件中，
    // 如果想绑定到指定的子组件标签上，则需要动态绑定

    // $listeners数组，父组件向子组件传递的所有事件方法

    // $slots，父组件向子组件传递的插槽内容

    // $scopedSlots 作用域插槽值

    // 弱引用的意思就是当对象本身赋值为null时，即使weakSet weakMap中的包含该对象，那么这个对象也会被js的垃圾回收。
    // let keyObj = {};
    // new WeakMap().set(
    //   keyObj,
    //   '13213'
    // )
    // keyObj=null

    // ondragstart - 用户开始拖动此元素时触发
    // ondrag - 此元素正在拖动时触发
    // ondragend - 用户完成此元素拖动后触发

    // ondragenter - 当被鼠标拖动的对象进入其容器范围内时触发此事件
    // ondragleave - 当被鼠标拖动的对象离开其容器范围内时触发此事件
    // ondragover - 当被鼠标拖动的对象在另一对象容器范围内移动时触发此事件
    // ondrop - 在一个拖动过程中，释放鼠标键时触发此事件

    /*多行文字垂直居中 （父元素 display: table，子元素display: table-cell , vertical-align: middle;）*/

    // ES6 规定，通过super调用父类的方法时，super会绑定子类的this。
    // super作为函数时,只能出现在子类的构造函数中
    // super作为对象时，指向父类的原型对象。

    // （执行上下文也叫执行环境）
    // 全局执行上下文，（window） 运行代码时，直接在执行栈底
    // 函数执行上下文， 只有在函数调用时才会进入执行栈

    // document.ondblclick = function(e) {
    //   const inp = document.querySelector('input');
    //   inp && inp.select();
    //   document.execCommand('copy'); //选择复制文本到粘贴板中
    // };

    // yield句本身没有返回值，或者说总是返回undefined。
    //next方法可以带一个参数，该参数就会被当作上一个yield语句后面的返回值。
    // 一次执行next方法 从上个yield语句执行到下个yield语句位置 返回对象中的value就是yield语句后面的值 ,
    // 当执行到return时,返回对象中的value就是return语句后面的返回值 并且done为true,
    // 生成器,返回一个迭代对象
    // function* funcc() {
    //   let a = 2 * (yield 1);
    //   // console.log(a);
    //   console.log(777);
    //   let b = yield 2;
    //   console.log(999);
    //   return 100;
    // }
    // let sad = funcc();
    // console.log(sad.next());
    // console.log(sad.next(111));
    // console.log(sad.next(333));

    /** 光影滑动效果：**/
    //设计一个透明层div 绝对定位 left为很大的负值，skewx在X轴上做了负25度的变形，
    //背景颜色用的是CSS3的线性渐变linear-gradient
    // background-image: linear-gradient(0deg,rgba(255,255,255,0),rgba(255,255,255,0.5),rgba(255,255,255,0));，
    //然后hover的时候，left移动过来 设置0.5s的动画时间。
    //同时在 i 层使用 cursor:pointer

    // 部分安卓手机点击图片会放大，如需要禁止放大，只需要设置 css 属性
    // 这个会让 img 标签的点击事件失效，如果想要给图片添加点击事件就要给上面再写一层
    // img{
    //     pointer-events: none;
    // }

    // const regHan = /^\p{sc=Han}+$/gu; //匹配汉字

    // UnionId是标识用户在一个开放平台中的唯一性的
    // AppID 和 AppSecret：公众号和小程序都会有一个 AppID 用来标识当前的微信应用
    // OpenID 为了识别用户，每个用户针对每个公众号或小程序等应用会产生一个安全的OpenID，用户在 可以理解 OpenID 是通过 AppID 和微信用户 ID 加密得到的

    // rem函数
    (function (doc, win) {
      var docEl = doc.documentElement,
        resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
        recalc = function () {
          var clientWidth = docEl.clientWidth;
          if (!clientWidth) return;
          docEl.style.fontSize = 37.5 * (clientWidth / 375) + 'px';

          // 安卓微信中字体调大后适配
          if (!doc.body) return;
          var d = doc.createElement('div');
          d.style.cssText = 'width:10rem;height:0;overflow: hidden;position:absolute;z-index:-1;visibility: hidden;';
          doc.body.appendChild(d);
          var dw = d.offsetWidth; // 1rem的实际展示px值
          doc.body.removeChild(d);
          var fz = 10 * (37.5 * (clientWidth / 375)); //正常计算出来的rem基准值 , 可自行修改为rem计算好的值
          var realRem = fz;
          if (dw !== fz) {
            //不相等 则被缩放了
            realRem = Math.pow(fz, 2) / dw / 10;
            docEl.style.fontSize = realRem + 'px';
          }
        };
      recalc();
      if (!doc.addEventListener) return;
      win.addEventListener(resizeEvt, recalc, false);
      doc.addEventListener('DOMContentLoaded', recalc, false);
    })(document, window);


    // new CustomEvent('myevent', {
    //   detail: {
    //     msg: 'hello world'
    //   }
    // });

    /**
     * 在 UTF-8 编码中 ASCII 字符（0x00 至 0x7F, 大部分字符）占用 1 个字节。大部分中文字符占用 3 个字节。
     * 获取字符串的字节长度 
     */
    function getTextSize(text) {
      const encoder = new TextEncoder();
      const uint8Array = encoder.encode(text, { transcodeTo: 'utf-8', });
      return uint8Array.length;
    }

    const text = "Hello, 世界!";
    const size = getTextSize(text);
    console.log(`字符串 "${text}" 的大小为 ${size} 字节`);


    // Math.floor(
    //   Math.random() * (max - min + 1)
    // ) + min



  </script>
</body>

</html>

<!-- html =  w / 1920  * 14 -->